﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Quest
{

    public partial class Form1 : Form
    {
        Game game;
        Random random = new Random();
        //GraphicsUnit units = GraphicsUnit.Point;

        Dictionary<string, PictureBox> pictureBoxEnemies = new Dictionary<string, PictureBox>();
        Dictionary<Weapons, PictureBox> pictureBoxWeapons = new Dictionary<Weapons, PictureBox>();
        Dictionary<Weapons, PictureBox> pictureBoxInventoryWeapons = new Dictionary<Weapons, PictureBox>();
        Dictionary<string, Label> labelEnemiesName = new Dictionary<string, Label>();
        Dictionary<string, Label> labelEnemiesHitPoints = new Dictionary<string, Label>();
        Dictionary<Direction, Button> buttonAttackDirections = new Dictionary<Direction, Button>();

        // enhancement #1: Make the enemies smarter
        //Dictionary<Type, Levels> enemiesLevels = new Dictionary<Type, Levels>();
        Dictionary<string, Label> labelEnemiesLevel = new Dictionary<string, Label>();

        bool Dance = false;
        double danceRadius;
        int danceCounter = 0;
        //private Rectangle dungeonBoundaries;

        public Form1()
        {
            InitializeComponent();
            this.Load += new EventHandler(this.Form1_Load);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // for future trials
            //dungeonBoundaries = new Rectangle(this.Location, this.BackgroundImage.Size);
            //game = new Game(dungeonBoundaries);                      
            game = new Game(new Rectangle(78, 57, 420, 155));
            SetControlsDictionaries(); // added to list and manage all controls
            game.NewLevel(random);
            UpdateCharacters();
        }

        public void SetControlsDictionaries()
        {
            string nameToSearch;

            #region Enemies
            // search Enemies controls
            foreach (Type type in game.AllEnemies)
            {
                nameToSearch = (type.Name).ToString();
                // search Enemies labels
                foreach (Label lb in this.tableLayoutPanelMain.Controls.OfType<Label>())
                    if (lb.Name.Contains(nameToSearch))
                        if (lb.Name.Contains("Hit"))
                            labelEnemiesHitPoints.Add(nameToSearch, lb);
                        else
                            labelEnemiesName.Add(nameToSearch, lb);

                // search Enemies Level labels
                foreach (Label lb in this.tableLayoutPanelEnemiesLevel.Controls.OfType<Label>())
                {
                    if (lb.Name.Contains(nameToSearch))
                    {
                        labelEnemiesLevel.Add(nameToSearch, lb);
                        break;
                    }
                }

                // search Enemies PictureBoxes
                foreach (PictureBox pb in this.Controls.OfType<PictureBox>())
                    if (pb.Name.Contains(nameToSearch))
                        pictureBoxEnemies.Add(nameToSearch, pb);
            }
            #endregion

            #region Weapons
            // search Weapons controls (PictureBoxes)
            foreach (Weapons weapon in Enum.GetValues(typeof(Weapons)))
            {
                nameToSearch = weapon.ToString();
                foreach (PictureBox pb in this.Controls.OfType<PictureBox>())
                    if (pb.Name.Contains(nameToSearch))
                        if (pb.Name.Contains("Inventory"))
                            pictureBoxInventoryWeapons.Add(weapon, pb);
                        else
                            pictureBoxWeapons.Add(weapon, pb);
            } 
            #endregion

            #region Attack buttons
            // search Attack Directions Buttons
            foreach (Direction direction in Enum.GetValues(typeof(Direction)))
            {
                nameToSearch = direction.ToString();
                foreach (Button pb in this.groupBoxAttack.Controls.OfType<Button>())
                    if (pb.Name.Contains(nameToSearch))
                        buttonAttackDirections.Add(direction, pb);
            } 
            #endregion

        }

        private void UpdateCharacters()
        {
            // update game level
            labelGameLevel.Text = "LEVEL: " + game.Level;
            // update Player
            pictureBoxPlayer.Location = game.PlayerLocation;
            labelPlayerHitPoints.Text = game.PlayerHitPoints.ToString();

            #region update Enemies
            // update Enemies
            int enemiesShown = 0;
            string nameToSearch;
            foreach (Type enemyType in game.AllEnemies)
            {
                Enemy enemy = game.getEnemyInGame(enemyType);
                nameToSearch = (enemyType.Name).ToString();
                PictureBox pictureBoxEnemy = pictureBoxEnemies[nameToSearch];
                Label labelEnemyName = labelEnemiesName[nameToSearch];
                Label labelEnemyHitPoints = labelEnemiesHitPoints[nameToSearch];
                Label labelEnemyLevel = labelEnemiesLevel[nameToSearch];

                UpDateEnemy(enemy, pictureBoxEnemy, labelEnemyName, labelEnemyHitPoints, labelEnemyLevel, ref enemiesShown);
            }
            
            #endregion

            //timer1.Start();

            #region update Weapon in room and Inventory
            // update Weapon in room
            foreach (Weapons weapon in pictureBoxWeapons.Keys)
                pictureBoxWeapons[weapon].Visible = false;

            PictureBox pb = pictureBoxWeapons[game.WeaponInRoom.Name];
            pb.Location = game.WeaponInRoom.Location;
            pb.Visible = !(game.WeaponInRoom.PickedUp);

            // update Inventory
            //Set the Visible property on each Inventory icon PictureBox
            //Check the Game object’s CheckPlayerInventory() method to figure out whether or not to display the various inventory icons.
            foreach (Weapons weapon in pictureBoxInventoryWeapons.Keys)
                pictureBoxInventoryWeapons[weapon].Visible = game.CheckPlayerInventory(weapon);            
            #endregion

            #region check game continuation conditions

            //check to see if the player died
            if (game.PlayerHitPoints <= 0)
            {
                timer1.Stop();
                DialogResult result;
                ReplayOrRestart myForm = new ReplayOrRestart();
                myForm.ShowDialog();
                result = myForm.DialogResult;
                myForm.Close();
                bool restartGame = (result == DialogResult.Ignore);
                bool repeatLevel = (result == DialogResult.Retry);
                if (restartGame || repeatLevel)
                {
                    game.NewLevel(random, restartGame, repeatLevel);
                    UpdateCharacters();
                    timer1.Start();
                }
                else
                    Application.Exit();
            }
            else
                pictureBoxPlayer.Visible = true;

            //checks to see if the player’s defeated all of the enemies.
            //If he has, then the player advances to the next level.
            if (enemiesShown < 1)
            {
                MessageBox.Show("You have defeated all enemies on this level");
                game.NewLevel(random);
                if (game.Level >= game.MaxLevel)
                    BeginDeathDance();
                else
                    UpdateCharacters();
            }
            
            #endregion
        }

        #region update Enemies helpers
        private void UpDateEnemy(Enemy enemy, PictureBox PictureBoxEnemy, Label labelEnemy, Label labelEnemyHitPoints, Label labelEnemyLevel, ref int enemiesShown)
        {
            int enemyHitPoints = 0;
            if (enemy != null)
            {
                enemyHitPoints = enemy.HitPoints;
                labelEnemyHitPoints.Text = enemyHitPoints.ToString();
                labelEnemyLevel.Text = game.EnemiesLevels[enemy.GetType()].ToString(); // enhancement #1: Make the enemies smarter
                PictureBoxEnemy.Location = enemy.Location;

                bool showEnemy = (enemy.HitPoints > 0);
            }
            if (enemyHitPoints > 0) enemiesShown++;
            ShowHideEnemy(PictureBoxEnemy, labelEnemy, labelEnemyHitPoints, labelEnemyLevel, enemyHitPoints > 0);
        }

        private void ShowHideEnemy(PictureBox PictureBoxEnemy, Label labelEnemy, Label labelEnemyHitPoints, Label labelEnemyLevel, bool showEnemy)
        {
            labelEnemyHitPoints.Visible = showEnemy;
            labelEnemy.Visible = showEnemy;
            labelEnemyLevel.Visible = showEnemy;
            PictureBoxEnemy.Visible = showEnemy;
        }

        // enhancement #1: Make the enemies smarter
        // show Enemies Levels
        //private void ShowEnemiesLevel()
        //{
        //    string nameToSearch;
        //    foreach (Label lb in this.tableLayoutPanelEnemiesLevel.Controls.OfType<Label>())
        //    {
        //        lb.Text = "";
        //        foreach (Type type in game.AllEnemies)
        //        {
        //            nameToSearch = (type.Name).ToString();
        //            if (lb.Name.Contains(nameToSearch))
        //            {
        //                lb.Text = game.EnemiesLevels[type].ToString();
        //                break;
        //            }
        //        }
        //    }
        //}

        //private void ShowEnemiesLevel()
        //{
        //    string nameToSearch;
        //    foreach (Type type in game.AllEnemies)
        //        foreach (Label lb in this.tableLayoutPanelEnemiesLevel.Controls.OfType<Label>())
        //        {
        //            lb.Text = "";
        //            nameToSearch = (type.Name).ToString();
        //            if (lb.Name.Contains(nameToSearch))
        //            {
        //                lb.Text = game.EnemiesLevels[type].ToString();
        //                break;
        //            }
        //        }
        //}

        #endregion

        #region Move buttons click event handlers
        //----------------------------------------------------
        // Move buttons click event handlers
        private void buttonMoveUp_Click(object sender, EventArgs e)
        {
            game.Move(Direction.Up, random);
            UpdateCharacters();
        }

        private void buttonMoveRight_Click(object sender, EventArgs e)
        {
            game.Move(Direction.Right, random);
            UpdateCharacters();
        }

        private void buttonMoveDown_Click(object sender, EventArgs e)
        {
            game.Move(Direction.Down, random);
            UpdateCharacters();
        }

        private void buttonMoveLeft_Click(object sender, EventArgs e)
        {
            game.Move(Direction.Left, random);
            UpdateCharacters();
        }
        //----------------------------------------------------
        #endregion

        #region Attack buttons event handlers
        //----------------------------------------------------
        // Attack buttons event handlers

        private void buttonAttackUp_Click(object sender, EventArgs e)
        {
            game.Attack(Direction.Up, random);
            UpdateCharacters();
        }

        private void buttonAttackRight_Click(object sender, EventArgs e)
        {
            game.Attack(Direction.Right, random);
            UpdateCharacters();
        }

        private void buttonAttackDown_Click(object sender, EventArgs e)
        {
            game.Attack(Direction.Down, random);
            UpdateCharacters();
        }

        private void buttonAttackLeft_Click(object sender, EventArgs e)
        {
            game.Attack(Direction.Left, random);
            UpdateCharacters();
        }
        //----------------------------------------------------
        #endregion
        
        #region Inventory item click event handlers and correlated methods
        //----------------------------------------------------
        // Inventory item click event handlers

        private void pictureBoxSwordInventory_Click(object sender, EventArgs e)
        {
            WeaponEquipAndSetInventoryPictureBoxBorderStyle(Weapons.Sword, sender);
        }

        private void pictureBoxBluePotionInventory_Click(object sender, EventArgs e)
        {
            WeaponEquipAndSetInventoryPictureBoxBorderStyle(Weapons.BluePotion, sender);
        }

        private void pictureBoxBowInventory_Click(object sender, EventArgs e)
        {
            WeaponEquipAndSetInventoryPictureBoxBorderStyle(Weapons.Bow, sender);
        }

        private void pictureBoxRedPotionInventory_Click(object sender, EventArgs e)
        {
            WeaponEquipAndSetInventoryPictureBoxBorderStyle(Weapons.RedPotion, sender);
        }

        private void pictureBoxMaceInventory_Click(object sender, EventArgs e)
        {
            WeaponEquipAndSetInventoryPictureBoxBorderStyle(Weapons.Mace, sender);
        }

        #region Inventory item click event handlers correlated methods
        // correlated methods
        private void WeaponEquipAndSetInventoryPictureBoxBorderStyle(Weapons weapon, object sender)
        {
            if (game.CheckPlayerInventory(weapon))
            {
                game.Equip(weapon);
                foreach (Weapons inventoryWeapon in pictureBoxInventoryWeapons.Keys)
                    if (sender.Equals(pictureBoxInventoryWeapons[inventoryWeapon]))
                        pictureBoxInventoryWeapons[inventoryWeapon].BorderStyle = BorderStyle.FixedSingle;
                    else
                        pictureBoxInventoryWeapons[inventoryWeapon].BorderStyle = BorderStyle.None;

                setInventoryAttackButtonsVisibility(!weapon.ToString().Contains("Potion"));
            }
        }

        private void setInventoryAttackButtonsVisibility(bool visible)
        {
            foreach (Direction direction in buttonAttackDirections.Keys)
                buttonAttackDirections[direction].Visible = visible;

            Button btn = buttonAttackDirections[Direction.Up];
            btn.Visible = true;
            btn.Text = Direction.Up.ToString();
            if (!visible)
                btn.Text = "Drink";
        } 
        #endregion



        //----------------------------------------------------
        #endregion

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (Dance == true)
                if (danceCounter <= 200/timer1.Interval)
                    DeathDance();
                else
                {
                    Dance = false;                    
                    MessageBox.Show("You Won!");
                    Application.Exit();
                }
        }

        #region DeathDance

        private void BeginDeathDance()
        {
            Point playerLocation = game.PlayerLocation;

            danceRadius = 1.5 * pictureBoxPlayer.Size.Height;
            int dx = 0;
            int dy = 0;
            double angle = Math.PI / 2;//0;
            double dAngle = 2 * Math.PI / 3;
            foreach (PictureBox pb in pictureBoxEnemies.Values)
            {
                dx = (int)(danceRadius * Math.Cos(angle));
                dy = (int)(danceRadius * Math.Sin(angle));
                pb.Location = new Point(playerLocation.X + dx, playerLocation.Y + dy);// = pictureBoxPlayer.Location.X + dx;
                pb.Visible = true;
                angle += dAngle;
            }

            Dance = true;
        }

        private void DeathDance()
        {
            Point playerLocation = game.PlayerLocation;
            double dx;
            double dy;
            double angle;
            double turnAngle = (Math.PI) / 180;
            int signX;
            int signY;
            foreach (PictureBox pb in pictureBoxEnemies.Values)
            {
                dx = pb.Location.X - playerLocation.X;
                dy = pb.Location.Y - playerLocation.Y;

                signX = 1;
                signY = 1;
                angle = Math.Atan(dy / dx);
                if (dx < 0)
                {
                    signY = -1;
                    signX = -1;
                }

                dx = Math.Round(danceRadius * Math.Cos(angle + turnAngle));
                dy = Math.Round(danceRadius * Math.Sin(angle + turnAngle));
                pb.Location = new Point(playerLocation.X + signX * (int)dx, playerLocation.Y + signY * (int)dy);// = pictureBoxPlayer.Location.X + dx;
                pb.Visible = true;
            }
            danceCounter++;

        }
        #endregion


    }
}
